package Network;

import Network.ClientNetworkLogic;
import baseservergrid.ModeCompilation;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.util.Iterator;
import java.util.LinkedList;
import javax.swing.JOptionPane;
import taskmanager.ITaskManager;

/**
 * Главный управляющий класс отвечающий за управление сетевыми подключениями и
 * сетевой активности
 * @author Volk ID = 1001
 */
public class NetworkLogic implements IModeWork/*, ModeCompilation*/{
  //----------------------------Константы класса--------------------------------
  /** Путь до файла настроек */
  private final String SETTINGS_PATH="./currentaddress.ini";
  /** Формат файла настроек */
  private final String FORMATV = "version 1.0";

  //----------------------------Аттрибуты класса--------------------------------
  private static int currentPort; //Порт для межсерверного взаимодействия
  private static String IP = "127.0.0.1"; //IP-адрес указываемый по умолчанию
  private s2sInnerLogic s2sLogic; //модуль межсерверного взаимодействия  
  private ClientNetworkLogic clientLogic; //модель взаимодействия с клиентами
  private static boolean isActivation = false; //Признак активации сети

  //Обработчики события: Переход сервера в автоматический режим
  static LinkedList<ActionListener> lstAutomaticModeListener = new LinkedList<ActionListener>();
  //----------------------------------------------------------------------------

  //----------------------Интерфейсные методы-----------------------------------
  /**
   * Конструктор класса сетевого соединения
   * @param taskmanager  - планировщик
   * @throws java.io.IOException ошибка чтения (или записи) конфигурации сети из файла
   * @throws Network.NeedActivationException первый запуск или один из конфигурационных файлов сети не найден
   */
    public NetworkLogic(ITaskManager taskmanager) throws IOException, NeedActivationException{
     try{
      load();
      //Инициализация сетевых модулей
      s2sLogic = new s2sInnerLogic(taskmanager);
      clientLogic = new ClientNetworkLogic();
     }catch(IOException ioe){
      currentPort =  Registry.REGISTRY_PORT;
      save();
      //Инициализация сетевых модулей
      s2sLogic = new s2sInnerLogic(taskmanager);
      clientLogic = new ClientNetworkLogic();
      throw new NeedActivationException(); //Первый запуск
     }
    }

    /**
     * Получить текущий порт по которому происходила активация
     * @return текущий порт
     */
    public static int getPort(){return currentPort;}

    /**
     * Получить текущий IP-адрес по которому происходила активация
     * @return текущий IP-адрес
     */
    public static String getIP(){return IP;}

   /**
   * @author Volk ID = 1001
   * Загрузить персональные данные из файла настроек
   * @throws java.io.IOException ошибка при работе с файлом (файл не найден, блокирован или имеет несовместимый или повреждённый формат)
   */
    public void load() throws IOException{
        //создать поток

       ObjectInputStream stream = new ObjectInputStream
            (new FileInputStream(SETTINGS_PATH));
       try{
            //Проверка на совпадение форматов
            String version = stream.readUTF();
            if(!version.equals(FORMATV)) throw new IOException();
            //Загружаем личные настройки
            IP = stream.readUTF();
            currentPort = stream.readInt();
       }finally{stream.close();}
    }

    /**
    * @author Volk ID = 1001
    * Сохранить параметры межсетевого взаимодействия в файле настроек
    * @throws java.io.IOException ошибка при работе с файлом
    */
    public void save() throws IOException{
        //создать поток
        ObjectOutputStream stream = new ObjectOutputStream
                (new FileOutputStream(SETTINGS_PATH));
        //сохранить записи
        try{
            //Сохраняем формат файла
            stream.writeUTF(FORMATV);
            //Сохраняем личные настройки
            stream.writeUTF(IP);
            stream.writeInt(currentPort);
            stream.flush();
        }finally{stream.close();}
    }

    /**
     * Сохранить все сведения о текущей машине (персональные сведения, как арбитра, как ведомого)
     * @throws java.io.IOException ошибка записи в файл
    */
    public void saveAll() throws IOException{
     save();
     s2sLogic.saveAll();
    }

    /**
     * Обновить адрес сервера
     * @param IP - новый айпишник
     * @param Port - новый порт
     * @return true - обновление завершилось успехом false - ничего не изменилось
     * @exception NeedActivationException - настройки обработались, но необходима переактивация
     */
    public boolean updateAddress(String IP, int Port) throws NeedActivationException, IOException {
      //Флаг говорящий о том, изменились ли настройки сети, если новые совпадают со старыми то обновление не нужно
      boolean isEqual = false;
      //Флаг говорящий о том, что обновление арбитра и синхронизация конфигурации с ведомым завершились успешно
      boolean isArbitratorMessage = true;
      if(this.IP.equals(IP) && (currentPort == Port)) isEqual = true;

      //Сохраняем текущие настройки - это необходимо так как нельзя реконфигурировать сервер на горячую
      String tempIP = this.IP;
      int tempPort = currentPort;

      try{

      //Изменяем текущие настройки
      currentPort = Port;
      this.IP = IP;
      //Если данные были обновлены, то сохраняем эти данные в конфигурационном файле
      if(!isEqual) save();
      //Сообщение об изменённых параметрах арбитру если сервер является ведомым
      if(s2sInnerLogic.mode == SLAVE_MODE){
         NetworkAddress oldAddress = new NetworkAddress(tempIP,tempPort);
         NetworkAddress newAddress = new NetworkAddress(IP, Port);
         SlaveServerController ssc = getS2S().getMySlaveController();
         IArbitrationController asc = ssc.getArbitratorController();
         asc.slaveUpdateConfig(oldAddress,newAddress);
      }
      //Если не произошла активация то сообщаем об этом пользователю
      if(!isActivation) throw new NeedActivationException();
       else{ //Восстанавливаем настройки актуальаные для текущей сессии
        currentPort = tempPort;
        this.IP = tempIP;
       }
      }catch(RemoteException re){
       isArbitratorMessage = false;
       JOptionPane.showMessageDialog(null, "Изменение конфигурации ведомого сервера возможно только при подключенном арбитре");
      }catch(IOException ioe){
       isArbitratorMessage = false;
       JOptionPane.showMessageDialog(null, "Ошибка сохранения в файл конфигурации - в обновлении отказано ");
      }
      //Если обновления арбитра или доступ к файлу провалились восстанавливаем исходные параметры
      if(!isArbitratorMessage){
       this.IP = tempIP;
       currentPort = tempPort;
       save();
      }

      return !isEqual;
    }

    /**
     * Активация сети
     * @return true - активация завершилась успешно, false - активация провалилась
     */


    public boolean activation(){
     if(!clientLogic.activation()) {
       //Случай когда порт уже занят
       isActivation = false;
       return isActivation;
     };  //активируем клиентский модуль
     if(!s2sLogic.activation()) return isActivation = false;; //активируем модуль межсерверного взаимодействия
     isActivation = true;  //сеть прошла активизацию!!!
     return true; 
    }
    
    /**
     * Получить контроллер межсерверного взаимодействия
     * @return контроллер межсерверного взаимодействия
     */
    public s2sInnerLogic getS2S(){return s2sLogic;}

    /**
     * Получить контроллер сетевого взаимодействия с клиентами
     * @return
     */
    public ClientNetworkLogic getClientNet(){return clientLogic;}

    /**
     * Проверить активирована ли сеть
     * @return true - сеть активировано, false - сеть не активирована
     */
    public static boolean isActivate(){return isActivation;}


      /**
   * Добавить обработчик события "Переход работы сервера в автоматический режим"
   * @param listener обработчик события
   */
  public static void addAutomaticModeListener(ActionListener listener){
   lstAutomaticModeListener.add(listener);
  }

  /**
   * Активизировать событие: "Переход работы сервера в автоматический режим"
   */
  public static void ActivateEventAutomaticMode(){
   Iterator<ActionListener> iter = lstAutomaticModeListener.iterator();
   while(iter.hasNext()){
    iter.next().actionPerformed(null);
   }
  }

   //---------------------------------------------------------------------------
}
